Raster animation
Raster graphics are not well optimized in the WLJS Notebook at the moment. Consider using them as sparingly as possible.
All raster graphics are rendered using the Image expression, which also supports updates via the Offload expression.
Let us start with a practical example — image distortion
Yandex Open Source Jam 2024
Or this one:
Stack Overflow OpenGL
Implementation
First, let's take an example image. Drag and drop it into the editor:
Then extract the raw data from it as a sequence of bytes:
img = ImageData[(* image *), "Byte"];
Now we can apply a mapping function to it, which stretches and shrinks pixels periodically:
shader = Compile[{{img, _Integer, 3}, {phase, _Real}}, Module[{iter},
With[{
ysize = Length[img],
xsize = Length[img[[1]]]
},
Table[
iter = 1.0;
Table[With[{
yr = y,
xr = If[# < 1, 1, If[# > xsize, xsize, #]] &@ Round[iter]
},
iter = iter + (1.0 + 0.7 Sin[6 Pi x / xsize + phase]);
img[[yr, xr]]
], {x, xsize}], {y, ysize}]
]
]];
Here, we use Compile
to speed up the process since only real arrays are involved. We do not apply any antialiasing filters; it works in the nearest neighbors approximation. Let's check the result using the original Byte
encoding:
Image[NumericArray[shader[img, 0], "UnsignedInteger8"], "Byte"]
Always provide a typed numeric array as the first argument to Image. By default, it assumes a Real
format for pixel data. Therefore, we explicitly specify the encoding by passing Byte
as the second argument.
The most efficient way is to use:
UnsignedInteger8
:Byte
Since performance is great with raster graphics, we can rely on fixed time intervals while animating, or use a straightforward Do
loop:
imageFrame = NumericArray[img, "UnsignedInteger8"];
Image[imageFrame // Offload, "Byte"]
Do[
imageFrame = NumericArray[shader[img, angle], "UnsignedInteger8"];
, {angle, 0, 4Pi, 2Pi/30.0}]
The resulting animation:
Alternative way
Using AnimationFrameListener one can get rid of any timers and animate as fast as system allows
frameEvent = CreateUUID[];
angle = 0.;
EventHandler[frameEvent, Function[Null,
imageFrame = NumericArray[shader[img, angle], "UnsignedInteger8"];
angle += 2Pi/30.0;
]];
EventFire[frameEvent, True];
Image[imageFrame // Offload, "Byte", Epilog->{
AnimationFrameListener[imageFrame // Offload, "Frame"->frameEvent]
}]
This way it is also suitable for Dynamic HTML